from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy import exc, select
from sqlalchemy.orm import selectinload

from schemas.schemas import UserCreate, UserOut, UserUpdate, UserAbsent
from db.db import AsyncSession, get_session
from models.models import User, Modality, DoctorAbsent
from db.users import pwd_context


user_router = APIRouter()


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24


@user_router.post("/", response_model=UserOut)
async def create_user(
    user: UserCreate,
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Создает пользователя."""
    new_user = User(
        username=user.username,
        login=user.login,
        hashed_password=pwd_context.hash(user.password),
        role_id=user.role_id,
        modality_id=user.modality_id,
        employment_rate=user.employment_rate,
        start_work=user.start_work,
        status=user.status,
    )
    session.add(new_user)
    try:
        await session.commit()
    except exc.IntegrityError:
        await session.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Пользователь уже существует",
        )
    await session.refresh(new_user)
    return new_user


@user_router.get("/", response_model=list[UserOut])
async def get_users(
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Возвращает всех пользователей."""
    query = select(User)
    result = await session.execute(query)
    users = result.scalars().all()
    return users


@user_router.delete("/{user_id}")
async def delete_user(
    user_id: int,
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Меняет статус пользователя на неактивный."""
    query = select(User).where(User.id == user_id)
    result = await session.execute(query)
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="Пользователь не найден.")
    user.is_active = False
    await session.commit()
    return {"message": "Пользователь установлен как неактивный."}


@user_router.get("/find_by_username")
async def find_by_username(
    username: str,
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Возвращает пользователя по имени."""
    query = (
        select(User)
        .options(
            selectinload(User.modality),
            selectinload(User.additional_modalities),
        )
        .where(
            User.username == username,
        )
    )
    result = await session.execute(query)
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="Пользователь не найден.")
    return {
        "id": user.id,
        "username": user.username,
        "modality": user.modality.id if user.modality else None,
        "employment_rate": user.employment_rate,
        "start_work": user.start_work,
        "status": user.status,
        "additional_modality": user.additional_modalities,
    }


@user_router.patch("/{user_id}", response_model=UserOut)
async def update_user(
    user_id: int,
    user_schema: UserUpdate,
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Изменяет данные пользователя."""
    query = (
        select(User)
        .options(
            selectinload(User.additional_modalities),
        )
        .where(User.id == user_id)
    )
    result = await session.execute(query)
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(status_code=404, detail="Пользователь не найден.")
    for field, value in user_schema.model_dump().items():
        if hasattr(user, field) and value is not None:
            setattr(user, field, value)
    await session.commit()

    if user_schema.additional_modality:
        add_modality = []
        for id_modality in user_schema.additional_modality:
            query = select(Modality).where(Modality.id == id_modality)
            result = await session.execute(query)
            modality = result.scalar_one_or_none()
            if modality:
                add_modality.append(modality)
        user.additional_modalities = add_modality
    await session.commit()
    await session.refresh(user)
    return user


@user_router.post('/absent/{user_fullname}')
async def user_absent(
    user_fullname: str,
    user_absent: UserAbsent,
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Получает данные об отсутствии пользователя."""
    query = select(
        User.id,
    ).where(
        User.username == user_fullname,
    )
    query_result = await session.execute(query)
    username = query_result.scalar()
    if not username:
        raise HTTPException(
            status=status.HTTP_404_BAD_REQUEST,
            detail="Пользователь не найден",
        )
    new_absent = DoctorAbsent(
        id_doctor=username,
        reason=user_absent.reason.value,
        start_absent=user_absent.start_absent,
        quantity_days=user_absent.quantity_days,
        viewed=False,
    )
    session.add(new_absent)
    await session.commit()
    await session.refresh(new_absent)
    return new_absent


@user_router.get('/absent/')
async def get_all_absent(
    token: Annotated[str, Depends(oauth2_scheme)],
    session: AsyncSession = Depends(get_session),
):
    """Возвращает все отсутствия."""
    query = select(
        DoctorAbsent
    ).options(
        selectinload(
            DoctorAbsent.users
        )
    ).where(
        DoctorAbsent.viewed == False,
    )
    query_result = await session.execute(query)
    all_absents = query_result.scalars().all()
    return [
        {
            'id': absent.id,
            'user': absent.users.username,
            'reason': absent.reason,
            'start_absent': absent.start_absent,
            'quantity_days': absent.quantity_days,
        }
        for absent in all_absents
    ]
